/* Copyright (c) 2021, LiWangQian<liwangqian@huawei.com> All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include <iostream>
#include <cstdint>

namespace demo {

class any {
public:
    ~any();

    template <typename T>
    any(T t);

    any(const any& other);

    template <typename T>
    any &operator=(T v);

    any &operator=(const any& other);

    template <typename T>
    T &as();

    template <typename T>
    const T &as() const;

private:
    struct holder {
        virtual ~holder() = default;
        virtual holder *clone() = 0;
    };

    template <typename V>
    struct value_holder : holder {
        value_holder(V v) : v{v} {}
        holder *clone() override
        {
            return new value_holder<V>{v};
        }

        V v;
    };

    holder *holder_{nullptr};
};

any::~any()
{
    if (holder_ != nullptr) {
        delete holder_;
    }
}

template <typename T>
any::any(T t)
{
    holder_ = new value_holder<T>{t};
}

any::any(const any& other)
{
    holder_ = other.holder_->clone();
}

template <typename T>
any &any::operator=(T v)
{
    delete holder_;
    holder_ = new value_holder<T>{v};
    return *this;
}

any &any::operator=(const any& other)
{
    if (&other == this) {
        return *this;
    }
    delete holder_;
    holder_ = other.holder_->clone();
    return *this;
}

template <typename T>
T &any::as()
{
    auto true_holder = dynamic_cast<value_holder<T>*>(holder_);
    if (true_holder != nullptr) {
        return true_holder->v;
    }
    throw std::bad_cast{};
}

template <typename T>
const T &any::as() const
{
    auto true_holder = dynamic_cast<const value_holder<T>*>(holder_);
    if (true_holder != nullptr) {
        return true_holder->v;
    }
    throw std::bad_cast{};
}

} // namespace demo

int main(int argc, char *argv[])
{
    using let = demo::any;

    let x = 123u;
    auto i = x.as<uint32_t>();
    std::cout << i << std::endl;
    let y = "hello world";
    std::cout << y.as<const char*>() << std::endl;
    y = 12.345f;
    std::cout << y.as<float>() << std::endl;
    let z = y;
    z.as<float>() += 100;
    std::cout << y.as<float>() << std::endl;
    std::cout << z.as<float>() << std::endl;
    return 0;
}
